﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Text;
using System.IO.Compression;

using System.Threading;
using System.Net.Sockets;
using System.Net;
using Microsoft.DirectX.DirectSound;
using Talk.Net.Library;

namespace Talk.Net
{
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class Window1 : Window
	{
		// DirectSound Module Level Objects

		private CaptureBufferDescription captureBufferDescription;
		private BufferDescription PlaybackBufferDescription;
		private SecondaryBuffer PlaybackBuffer;
		private Device PlaybackDevice;
		private WaveFormat waveFormat;
		private CaptureDevice mic;
		private int PlaybackPosition, WritePosition;
		private byte[] localBuffer;
		private byte[] silence;

		// Networking Module Level Objects
		private IPEndPoint clientEndPoint;
		private Socket clientSocket;

		private Boolean shuttingDown;

		// Thread Module Level Objects
		private delegate void ProcessContentDelegate(byte[] audio);
		ProcessContentDelegate processContentDelegate;

		public Window1()
		{
			InitializeComponent();
			LoadSettings();
			Initialize();

			silence = new byte[5512];
		}

		public void Initialize()
		{
			waveFormat = new WaveFormat();
			captureBufferDescription = new CaptureBufferDescription();
			captureBufferDescription.Format = waveFormat;
			processContentDelegate = new ProcessContentDelegate(ProcessContent);

			List<Server> servers = ServerManager.GetServers();
			comboServers.ItemsSource = servers;

			if (servers.Count > 0)
			{
				comboServers.SelectedIndex = 0;
			}

			mic = new CaptureDevice();
			mic.ContentNotification += new CaptureDevice.ContentNotifierDelegate(mic_ContentNotification);

			localBuffer = new byte[8024];
		}

		private void ProcessContent(byte[] content)
		{
			// noise cancellation play time
			// content = DigitalSignalProcessor.CleanseA(content, 1024);

			// send to server
			if (clientSocket != null)
			{
				// byte[] processed = EncodingManager.Encode(content);
				clientSocket.Send(content);
			}
			else
			{
				EncodingManager.Compress2(content);
				// byte[] processed = EncodingManager.Encode(content);
				PlaybackBuffer.GetCurrentPosition(out PlaybackPosition, out WritePosition);
				PlaybackBuffer.Write(WritePosition, content, LockFlag.None);
			}
		}

		private void StartBufferManager()
		{
			ThreadStart BufferManagement = new ThreadStart(ManageBuffer);
			Thread BufferManagementThread = new Thread(BufferManagement);
			BufferManagementThread.Start();
		}

		// this function to stop the playback of the directx buffer when we have reached
		// the end of the current content
		private void ManageBuffer()
		{
			while (!shuttingDown)
			{
				if (PlaybackPosition > WritePosition)
					PlaybackBuffer.Stop();

				// should we also zero out the buffer?

				Thread.Sleep(200);
			}
		}

		void mic_ContentNotification(byte[] content)
		{
			Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, processContentDelegate, content);
		}

		private void LoadSettings()
		{
			// Device Settings
			CaptureDevicesCollection inputdevices = new CaptureDevicesCollection();
			InputDeviceListing.ItemsSource = inputdevices;
			InputDeviceListing.DisplayMemberPath = "Description";

			if (inputdevices.Count > 0)
			{
				InputDeviceListing.SelectedIndex = 0;
			}
			else
			{
				System.Windows.MessageBox.Show("No input devices found.");
			}

			DevicesCollection outputdevices = new DevicesCollection();
			OutputDeviceListing.ItemsSource = outputdevices;
			OutputDeviceListing.DisplayMemberPath = "Description";

			if (outputdevices.Count > 0)
			{
				OutputDeviceListing.SelectedIndex = 0;
			}
			else
			{
				System.Windows.MessageBox.Show("No output devices found.");
			}
		}

		private void ReceiveData(IAsyncResult ar)
		{
			try
			{
				if (!shuttingDown)
				{
					Socket socket = (Socket)ar.AsyncState;

					// move the content to the manager
					int bytes = socket.EndReceive(ar);

					if (bytes > 0)
					{
						byte[] temp = new byte[bytes];
						for (int i = 0; i < bytes; i++)
						{
							temp[i] = localBuffer[i];
						}

						// do something with the buffer
					}

					int available = socket.Available;

					socket.BeginReceive(localBuffer, 0, available, SocketFlags.None, new AsyncCallback(ReceiveData), socket);
				}
			}
			catch (System.Net.Sockets.SocketException se)
			{

			}
		}

		private void SetupPlaybackDevice()
		{
			// playback buffer
			PlaybackDevice = new Device();
			IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
			PlaybackDevice.SetCooperativeLevel(mainWindowPtr, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);

			PlaybackBufferDescription = new BufferDescription();
			PlaybackBufferDescription.Format = FormatManager.GetFormat();
			PlaybackBufferDescription.DeferLocation = true;
			PlaybackBufferDescription.BufferBytes = PlaybackBufferDescription.Format.AverageBytesPerSecond * 10;
			PlaybackBufferDescription.GlobalFocus = true;

			PlaybackBuffer = new SecondaryBuffer(PlaybackBufferDescription, PlaybackDevice);
			PlaybackBuffer.Play(0, BufferPlayFlags.Looping);
		}

		#region GUI Events

		private void button1_Click(object sender, RoutedEventArgs e)
		{
			SetupPlaybackDevice();
			StartBufferManager();
		}

		private void titleBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragMove();
		}

		private void buttonExit_Click(object sender, RoutedEventArgs e)
		{
			shuttingDown = true;
			if (clientSocket != null)
			{
				clientSocket.Close();
			}

			mic.Shutdown();

			System.Windows.Application.Current.Shutdown(0);
		}

		private void buttonSend_Click(object sender, RoutedEventArgs e)
		{
			if (clientSocket != null)
			{
				WaveFormat format = FormatManager.GetFormat();
				int length = format.AverageBytesPerSecond * 1;
				byte[] sample = new byte[length];

				// create a tone using sin function
				double A = 697 * 2 * Math.PI / (double)format.SamplesPerSecond;
				for (int i = 0; i < length; i++)
				{
					sample[i] = (byte)(format.BitsPerSample * (Math.Sin(i * A)));
				}

				clientSocket.Send(sample);
			}
		}

		private void buttonRecord_Click(object sender, RoutedEventArgs e)
		{
			mic.Capture();
		}

		private void buttonTest_Click(object sender, RoutedEventArgs e)
		{
			Microsoft.DirectX.DirectSound.Device device = new Microsoft.DirectX.DirectSound.Device();
			IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
			device.SetCooperativeLevel(mainWindowPtr, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);

			short channels = 1;
			short bitsPerSample = 8;

			// 11KHz use 11025 , 22KHz use 22050, 44KHz use 44100, etc.
			int samplesPerSecond = 44100;

			WaveFormat format = new WaveFormat();
			format.BitsPerSample = bitsPerSample;
			format.Channels = channels;
			format.BlockAlign = (short)(channels * (bitsPerSample / (short)8));
			format.FormatTag = WaveFormatTag.Pcm;
			format.SamplesPerSecond = samplesPerSecond;
			format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;

			int length = format.AverageBytesPerSecond * 1;
			byte[] sample = new byte[length];

			double A = 697 * 2 * Math.PI / (double)samplesPerSecond;
			for (int i = 0; i < length; i++)
			{
				sample[i] = (byte)(bitsPerSample * (Math.Sin(i * A)));
			}

			BufferDescription description = new BufferDescription();
			description.DeferLocation = true;
			description.BufferBytes = sample.Length;
			description.Format = format;

			SecondaryBuffer buffer = new SecondaryBuffer(description, device);
			buffer.Write(0, sample, LockFlag.EntireBuffer);
			buffer.Play(0, BufferPlayFlags.Default);
		}

		private void buttonConnect_Click(object sender, RoutedEventArgs e)
		{
			if (comboServers.SelectedItem != null)
			{
				Server currentServer = (Server)comboServers.SelectedItem;
				clientEndPoint = new IPEndPoint(IPAddress.Parse(currentServer.address), currentServer.port);
				clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

				try
				{
					clientSocket.Connect(clientEndPoint);
					clientSocket.BeginReceive(localBuffer, 0, 0, SocketFlags.None, new AsyncCallback(ReceiveData), clientSocket);
				}
				catch (Exception ex)
				{
					System.Windows.Forms.MessageBox.Show(ex.Message);
				}
			}
		}
		#endregion

		private void button2_Click(object sender, RoutedEventArgs e)
		{
			byte[] test = new byte[4];
			test[0] = (byte)128;
			test[1] = (byte)129;
			test[2] = (byte)130;
			test[3] = (byte)131;

			EncodingManager.Compress(test);
		}

	}
}
